package com.jackchong.net

import android.util.Log
import com.blankj.utilcode.util.NetworkUtils
import com.jackchong.utils.JLog
import io.reactivex.Observable
import io.reactivex.ObservableTransformer
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.*
import java.lang.RuntimeException

/**
 * author : jack(黄冲)
 * e-mail : 907755845@qq.com
 * create : 2020/7/28
 * desc   :
 */
object JNet {

    private val retry: Retry by lazy { Retry.default() }

    init {
        NetworkUtils.registerNetworkStatusChangedListener(NetwordStatusObserver { retry.renet() })
    }

    val handler = CoroutineExceptionHandler { _, exception ->
        JLog.e("error", Log.getStackTraceString(exception))
        throw RuntimeException(exception.message)
    }

    fun <T, J: JBaseData<T>> net(api: Observable<J>, param: JNetParam<T, J> = JNetParam.build {  }){
        if (param.dispatchMode == NetDispatchMode.SINGLE && NetCache.contain(api.hashCode())) {
            return
        }
        api.compose(main())
            .doOnSubscribe {
                param.loading.create()
                param.cache.read {
                    param.loading.cancel()
                    param.onNext.invoke(it as T)
                    param.onComplete()
                }
            }
            .retryWhen(RetryWithDelay(0))
            .`as`(param.view.bindAutoDispose())
            .subscribe({
                param.loading.cancel()
                NetCache.remove(api.hashCode())
                when (it.getNetAction()) {
                    ActionModeEnum.SUCCESS -> {
                        param.onNext(it.data())
                        param.onComplete()
                        param.cache.write(it.data())
                    }
                    else -> {
                        param.onFail(it)
                        param.onComplete()
                        param.view.onFill(it)
                    }
                }
            }, {
                param.loading.cancel()
                retry.catchThrowableAdd(it, param)
                param.onError(it)
                param.view.onError(it)
                param.onComplete()
                JLog.e("jhttp", Log.getStackTraceString(it))
            })

    }

    private fun <T> main(): ObservableTransformer<T, T>? {
        return ObservableTransformer { upstream: Observable<T> ->
            upstream.subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
        }
    }


/*
    fun <T: BaseBean> net(param: JNetParam<T> = JNetParam.build {  }){
        var loadingJob: Job? = null

        var targetKey = ""
        for (st in Thread.currentThread().stackTrace) {
            if (st.isNativeMethod) {
                continue
            }
            if (st.className == Thread::class.java.name) {
                continue
            }
            if (st.className == JNet::class.java.name) {
                continue
            }
            targetKey = st.className + st.methodName
            break
        }

        CoroutineScope(Dispatchers.Main).launch {

            async(Dispatchers.IO) {
                if (param.cachePlaceholder && targetKey.isNotEmpty()) {
                    CacheDiskUtils.getInstance().getSerializable(targetKey)?.also {
                        withContext(Dispatchers.Main) {
                            param.onNext.invoke(it as T)
                        }
                    }
                }
            }



            param.api.asFlow().catch {
                if (param.networkChangeAutoRetry && it is UnknownHostException || it is SocketTimeoutException || it is NoRouteToHostException || it is ConnectException){
                    failQueue.add(param.apply { cachePlaceholder = false })
                }
                param.error.invoke(it)
                LogUtils.e("jhttp", "异常", Log.getStackTraceString(it))
            }.onStart {
                loadingJob = CoroutineScope(Dispatchers.Main).launch {
                    val delayMs = when (param.loadingMode) {
                        NetLoadingMode.SHOW -> 0L
                        NetLoadingMode.DELAY -> 500L
                        NetLoadingMode.HIDE -> -1L
                    }
                    if (delayMs != -1L){
                        delay(delayMs)
                        param.view.onShowLoading()
                    }
                }.lifeRecycle(param.view.lifecycle())
            }.onCompletion {
                loadingJob?.cancel()
                param.view.onHideLoading()
            }.onEach {
                if (it.succeed()) {
                    param.onNext.invoke(it.data)
                    async(Dispatchers.IO) {
                        if (param.cachePlaceholder && targetKey.isNotEmpty()) {
                            CacheDiskUtils.getInstance().put(targetKey, it.data)
                        }
                    }
                }else{
                    param.onFail.invoke(it)
                }
            }.catch {
                param.error.invoke(it)
            }.collectIndexed { index, value ->
//                if (it.succeed()) {
//                    param.onNext.invoke(it.data)
//                    async(Dispatchers.IO) {
//                        if (param.cachePlaceholder && targetKey.isNotEmpty()) {
//                            CacheDiskUtils.getInstance().put(targetKey, it.data)
//                        }
//                    }
//                }else{
//                    param.onFail.invoke(it)
//                }
            }
        }.lifeRecycle(param.view.lifecycle())
    }
*/

}